/*
 * @Author: Wangjun
 * @Date: 2023-09-22 21:13:39
 * @LastEditTime: 2023-10-24 16:29:57
 * @LastEditors: Wangjun
 * @Description:data set 有多个时刻的数据
 * @FilePath: \xr_node_calcd:\go\src\gitee.com\haodreams\libs\ds\dataset.go
 * hnxr
 */
package ds

import (
	"bufio"
	"os"
	"strings"

	"gitee.com/haodreams/libs/easy"
)

type number interface {
	int64 | int32 | int16 | int8 | uint64 | uint32 | uint16 | uint8 | int | uint | float32 | float64
}

// 这是一个三维数组
// 第一维 时间排序的表
// 第二维 时刻对齐全部测点的二维表
// 第三维 一个设备的行数据
type Dataset[N number] struct {
	Category    string //类别
	Begin       int64  //起始位置
	End         int64  //结束时间
	Step        int64  //步长
	titles      []string
	lowerTitles []string
	alias       []string
	ids         []int
	records     []TsRecords[N] //按时刻对齐的数据
	mapTitle    map[string]int
	mapAlias    map[string]int
	mapID       map[int]int
}

/**
 * @description:这个函数必须第一次调用
 * @param {[]string} titles
 * @return {*}
 */
func (m *Dataset[N]) Setup(ids []int, titles []string) {
	if len(ids) == 0 || len(titles) == 0 {
		if len(m.records) > 0 {
			m.records = m.records[:0]
		}
		return
	}
	m.mapID = make(map[int]int)
	m.mapTitle = make(map[string]int)
	m.mapAlias = make(map[string]int)
	m.titles = titles
	m.alias = m.titles
	m.lowerTitles = make([]string, len(titles))
	for i, title := range titles {
		m.mapTitle[title] = i
		m.mapAlias[title] = i
		m.lowerTitles[i] = strings.ToLower(title)
	}
	m.ids = ids
	for i, id := range ids {
		m.mapID[id] = i
	}
}

/**
 * @description: 获取id
 * @return {*}
 */
func (m *Dataset[N]) GetIDs() []int {
	return m.ids
}

/**
 * @description: 获取标题
 * @return {*}
 */
func (m *Dataset[N]) Titles() []string {
	return m.titles
}

/**
 * @description: 获取小写的标题
 * @return {*}
 */
func (m *Dataset[N]) LowerTitles() []string {
	return m.lowerTitles
}

/**
 * @description: 获取记录集的个数
 * @return {*}
 */
func (m *Dataset[N]) RecordsetCount() int {
	return len(m.records)
}

/**
 * @description: 根据开始时间，结束时间， 步长自动计算需要建立多少个记录集
 * @param {*} bt
 * @param {*} et
 * @param {int64} step
 * @return {*}
 */
func (m *Dataset[N]) SetCountByTime(bt, et, step int64) {
	count := (et - bt) / step
	if (et-bt)%step != 0 {
		count++
	}

	m.Begin = bt
	m.End = et
	m.Step = step
	m.SetCount(int(count))
}

/**
 * @description: 设置记录集的个数
 * @param {int} count
 * @return {*}
 */
func (m *Dataset[N]) SetCount(count int) {
	nColumn := len(m.titles)
	if cap(m.records) < count {
		m.records = make([]TsRecords[N], count)
	} else {
		m.records = m.records[:count]
	}
	for i := range m.records {
		m.records[i].Setup(m.ids, nColumn)
	}
}

/**
 * @description: 获取标题所在的列
 * @param {string} name
 * @return {*}
 */
func (m *Dataset[N]) GetColumnIndex(name string) int {
	i, ok := m.mapTitle[name]
	if ok {
		return i
	}
	return -1
}

/**
 * @description: 根据id获取每个rs的行号
 * @param {int} id
 * @return {*}
 */
func (m *Dataset[N]) GetRowIndexFromID(id int) int {
	nRow, ok := m.mapID[id]
	if !ok {
		return -1
	}
	return nRow
}

/**
 * @description: 根据时间获取一个记录集
 * @param {int64} t
 * @return {*}
 */
func (m *Dataset[N]) GetRecordsetByTime(t int64) *TsRecords[N] {
	return m.GetRecordset(m.GetIndexFromTime(t))
}

/**
 * @description: 获取一个时刻记录集
 * @param {int} nRow
 * @return {*}
 */
func (m *Dataset[N]) GetRecordset(idx int) *TsRecords[N] {
	if idx >= len(m.records) || idx < 0 {
		return nil
	}
	return &m.records[idx]
}

/**
 * @description: 设置指定时间，指定行的值
 * @param {int64} t 选择的指定时间
 * @param {int} nRow 选择指定的行
 * @param {[]*N} vals 需要设置的值
 * @return {*}
 */
func (m *Dataset[N]) SetRowByTime(t int64, nRow int, vals []*N) {
	rs := m.GetRecordsetByTime(t)
	if rs == nil {
		return
	}

	row := rs.GetRow(nRow)
	if row == nil {
		return
	}

	row.SetValues(vals)
}

/**
 * @description: 设置一个单元格的值
 * @param {int64} t 指定时间
 * @param {int} id 指定id
 * @param {string} title 指定标题
 * @param {*N} val
 * @return {*}
 */
func (m *Dataset[N]) SetCell(t int64, id int, title string, val *N) {
	rs := m.GetRecordsetByTime(t)
	if rs == nil {
		return
	}

	nRow, ok := m.mapID[id]
	if !ok {
		return
	}

	row := rs.GetRow(nRow)
	if row == nil {
		return
	}

	nColumn, ok := m.mapTitle[title]
	if !ok {
		return
	}

	row.SetValue(nColumn, val)
}

/**
 * @description: 设置一个单元格的值
 * @param {int64} t 指定时间
 * @param {int} id 指定id
 * @param {string} title 指定标题
 * @param {*N} val
 * @return {*}
 */
func (m *Dataset[N]) FastSetCell(t int64, nRow int, title string, val *N) {
	rs := m.GetRecordsetByTime(t)
	if rs == nil {
		return
	}

	row := rs.GetRow(nRow)
	if row == nil {
		return
	}

	nColumn, ok := m.mapTitle[title]
	if !ok {
		return
	}

	row.SetValue(nColumn, val)
}

/**
 * @description: 设置一个单元格的值
 * @param {int64} t 指定时间
 * @param {int} id 指定id
 * @param {string} nColumn 指定列
 * @param {*N} val
 * @return {*}
 */
func (m *Dataset[N]) FastestSetCell(t int64, nRow, nColumn int, val *N) {
	rs := m.GetRecordsetByTime(t)
	if rs == nil {
		return
	}

	row := rs.GetRow(nRow)
	if row == nil {
		return
	}

	row.SetValue(nColumn, val)
}

/**
 * @description: 时间转为下标
 * @param {int64} pos
 * @return {*}
 */
func (m *Dataset[N]) GetIndexFromTime(t int64) int {
	t = (t - m.Begin) / m.Step
	return int(t)
}

/**
 * @description: 获取原始时间
 * @param {int} idx
 * @return {*}
 */
func (m *Dataset[N]) GetTime(idx int) int64 {
	return (int64(idx) * m.Step) + m.Begin
}

/**
 * @description: 设置列的别名
 * @param {int} idx
 * @param {string} alias
 * @return {*}
 */
func (m *Dataset[N]) SetAlias(idx int, alias string) {
	if idx >= len(m.titles) || idx < 0 {
		return
	}
	m.alias[idx] = alias

	for _, v := range m.mapAlias {
		if v == idx {
			m.mapAlias[alias] = idx
		}
	}
}

/**
 * @description: 设置全部的别名
 * @param {[]string} aliases
 * @return {*}
 */
func (m *Dataset[N]) SetAllAlias(aliases []string) {
	copy(m.alias, aliases)
	m.mapAlias = make(map[string]int)
	for i, alias := range m.alias {
		m.mapAlias[alias] = i
	}
}

func (m *Dataset[N]) SaveCsv(path string) (err error) {
	f, err := os.Create(path)
	if err != nil {
		return
	}
	defer f.Close()
	buf := bufio.NewWriter(f)
	defer buf.Flush()
	buf.WriteString("ts,id,")
	buf.WriteString(strings.Join(m.titles, ","))
	buf.WriteByte('\n')
	for i := range m.records {
		ts := m.Begin + int64(i)*m.Step
		header := easy.FormatTime(ts) + ","
		m.records[i].WriteTo(buf, []byte(header), false)
	}
	return
}
